#pragma once
#include "../Ode.h"

namespace Skill
{
	namespace Ode
	{	
		///<summary>
		/// class numbers - each geometry object needs a unique number
		///</summary>
		public enum class OdeClassses : int
		{
			SphereClass = dSphereClass,
			BoxClass = dBoxClass,
			CapsuleClass = dCapsuleClass,
			CylinderClass = dCylinderClass,
			PlaneClass = dPlaneClass,
			RayClass = dRayClass,
			ConvexClass = dConvexClass,
			GeomTransformClass = dGeomTransformClass,
			TriMeshClass = dTriMeshClass,
			HeightfieldClass = dHeightfieldClass,

			FirstSpaceClass = dFirstSpaceClass,
			SimpleSpaceClass = dSimpleSpaceClass,
			HashSpaceClass = dHashSpaceClass,
			SweepAndPruneSpaceClass = dSweepAndPruneSpaceClass, // SAP
			QuadTreeSpaceClass = dQuadTreeSpaceClass,
			LastSpaceClass = dLastSpaceClass,

			FirstUserClass = dFirstUserClass,
			LastUserClass = dLastUserClass,
			GeomNumClasses = dGeomNumClasses
		};

		public ref class DOde abstract sealed
		{
		private:
			static bool isInitialized;
		public:
			///<summary>
			///brief Library initialization flags.
			///
			/// These flags define ODE library initialization options.
			///
			/// @c dInitFlagManualThreadCleanup indicates that resources allocated in TLS for threads
			/// using ODE are to be cleared by library client with explicit call to @c dCleanupODEAllDataForThread.
			/// If this flag is not specified the automatic resource tracking algorithm is used.
			///
			/// With automatic resource tracking, On Windows, memory allocated for a thread may 
			/// remain not freed for some time after the thread exits. The resources may be 
			/// released when one of other threads calls @c dAllocateODEDataForThread. Ultimately,
			/// the resources are released when library is closed with @c dCloseODE. On other 
			/// operating systems resources are always released by the thread itself on its exit
			/// or on library closure with @c dCloseODE.
			///
			/// If @c dInitFlagManualThreadCleanup was not specified during initialization,
			/// calls to @c dCleanupODEAllDataForThread are not allowed.
			///
			/// @see dInitODE2
			/// @see dAllocateODEDataForThread
			/// @see dCloseODE
			/// @ingroup init
			///</summary>
			enum class DInitializeOdeFlags 
			{
				ManualThreadCleanup = dInitFlagManualThreadCleanup, //@< Thread local data is to be cleared explicitly on @c dCleanupODEAllDataForThread function call
			};
			

			///<summary>
			/// @brief Initializes ODE library.
			///
			/// @c dInitODE is obsolete. @c dInitODE2 is to be used for library initialization.
			///
			/// A call to @c dInitODE is equal to the following initialization sequence
			/// @code
			///     dInitODE2(0);
			///     dAllocateODEDataForThread(dAllocateMaskAll);
			/// @endcode
			///
			/// @see dInitODE2
			/// @see dAllocateODEDataForThread
			/// @ingroup init
			///</summary>
			static void Initialize()
			{
				dInitODE();
				isInitialized = true;
			}

			static property bool IsInitialized
			{
				bool get(){return isInitialized;}
			}

			///<summary>
			/// @brief Initializes ODE library.
			/// @param uiInitFlags Initialization options bitmask
			/// @return A nonzero if initialization succeeded and zero otherwise.
			///
			/// This function must be called to initialize ODE library before first use. If 
			/// initialization succeeds the function may not be called again until library is 
			/// closed with a call to @c dCloseODE.
			///
			/// The @a uiInitFlags parameter specifies initialization options to be used. These
			/// can be combination of zero or more @c dInitODEFlags flags.
			///
			/// @see dInitODEFlags
			/// @see dCloseODE
			/// @ingroup init
			///</summary>			
			static void Initialize2(DInitializeOdeFlags uiInitFlags)
			{
				dInitODE2((unsigned int)uiInitFlags);
				isInitialized = true;
			}
			static void Initialize2(unsigned int uiInitFlags)
			{
				dInitODE2(uiInitFlags);
				isInitialized = true;
			}

			///<summary>
			/// @brief ODE data allocation flags.
			///
			/// These flags are used to indicate which data is to be pre-allocated in call to
			/// @c dAllocateODEDataForThread.
			///
			/// @c dAllocateFlagBasicData tells to allocate the basic data set required for
			/// normal library operation. This flag is equal to zero and is always implicitly 
			/// included.
			///
			/// @c dAllocateFlagCollisionData tells that collision detection data is to be allocated.
			/// Collision detection functions may not be called if the data has not be allocated 
			/// in advance. If collision detection is not going to be used, it is not necessary
			/// to specify this flag.
			///
			/// @c dAllocateMaskAll is a mask that can be used for for allocating all possible 
			/// data in cases when it is not known what exactly features of ODE will be used.
			/// The mask may not be used in combination with other flags. It is guaranteed to
			/// include all the current and future legal allocation flags. However, mature 
			/// applications should use explicit flags they need rather than allocating everything.
			///
			/// @see dAllocateODEDataForThread
			/// @ingroup init
			///</summary>
			enum class DAllocateOdeDataFlags
			{
				///<summary>
				/// Allocate basic data required for library to operate
				///</summary>
				BasicData = dAllocateFlagBasicData,

				///<summary>
				/// Allocate data for collision detection
				///</summary>
				CollisionData = dAllocateFlagCollisionData,

				///<summary>
				/// Allocate all the possible data that is currently defined or will be defined in the future. 
				///</summary>
				MaskAll = dAllocateMaskAll
			};

			///<summary>
			/// @brief Allocate thread local data to allow the thread calling ODE.
			/// @param uiAllocateFlags Allocation options bitmask.
			/// @return A nonzero if allocation succeeded and zero otherwise.
			/// 
			/// The function is required to be called for every thread that is going to use
			/// ODE. This function allocates the data that is required for accessing ODE from 
			/// current thread along with optional data required for particular ODE subsystems.
			///
			/// @a uiAllocateFlags parameter can contain zero or more flags from @c dAllocateODEDataFlags
			/// enumerated type. Multiple calls with different allocation flags are allowed.
			/// The flags that are already allocated are ignored in subsequent calls. If zero
			/// is passed as the parameter, it means to only allocate the set of most important
			/// data the library can not operate without.
			///
			/// If the function returns failure status it means that none of the requested 
			/// data has been allocated. The client may retry allocation attempt with the same 
			/// flags when more system resources are available.
			///
			/// @see dAllocateODEDataFlags
			/// @see dCleanupODEAllDataForThread
			/// @ingroup init
			///</summary>
			static int AllocateDataForThread(DAllocateOdeDataFlags uiAllocateFlags)
			{
				return dAllocateODEDataForThread((unsigned int)uiAllocateFlags);
			}

			///<summary>
			/// @brief Free thread local data that was allocated for current thread.
			///
			/// If library was initialized with @c dInitFlagManualThreadCleanup flag the function 
			/// is required to be called on exit of every thread that was calling @c dAllocateODEDataForThread.
			/// Failure to call @c dCleanupODEAllDataForThread may result in some resources remaining 
			/// not freed until program exit. The function may also be called when ODE is still 
			/// being used to release resources allocated for all the current subsystems and 
			/// possibly proceed with data pre-allocation for other subsystems.
			///
			/// The function can safely be called several times in a row. The function can be 
			/// called without prior invocation of @c dAllocateODEDataForThread. The function 
			/// may not be called before ODE is initialized with @c dInitODE2 or after library 
			/// has been closed with @c dCloseODE. A call to @c dCloseODE implicitly releases 
			/// all the thread local resources that might be allocated for all the threads that 
			/// were using ODE.
			///
			/// If library was initialized without @c dInitFlagManualThreadCleanup flag 
			/// @c dCleanupODEAllDataForThread must not be called.
			///
			/// @see dAllocateODEDataForThread
			/// @see dInitODE2
			/// @see dCloseODE
			/// @ingroup init
			///</summary>

			static void CleanupAllDataForThread()
			{
				void dCleanupODEAllDataForThread();
			}


			///<summary>
			/// @brief Close ODE after it is not needed any more.
			///
			/// The function is required to be called when program does not need ODE features any more.
			/// The call to @c dCloseODE releases all the resources allocated for library
			/// including all the thread local data that might be allocated for all the threads
			/// that were using ODE.
			///
			/// @c dCloseODE is a paired function for @c dInitODE2 and must only be called
			/// after successful library initialization.
			///
			/// @note Important!
			/// Make sure that all the threads that were using ODE have already terminated 
			/// before calling @c dCloseODE. In particular it is not allowed to call
			/// @c dCleanupODEAllDataForThread after @c dCloseODE.
			///
			/// @see dInitODE2
			/// @see dCleanupODEAllDataForThread
			/// @ingroup init
			///</summary>
			static void Close()
			{				
				if(DRegisteredObjects::objects->Count > 0)
				{
					System::Text::StringBuilder^ builder = gcnew System::Text::StringBuilder();
					for each (DIRegisteredObject^ o in DRegisteredObjects::objects->Values)
					{
						builder->AppendLine(o->ToString() + ((String::IsNullOrEmpty(o->Name))? "" :" : " + o->Name));
					}
					throw gcnew System::ApplicationException("You must dispose all registered objects before close ode\n" + DRegisteredObjects::objects->Count.ToString() +" Remained objects are \n" + builder->ToString());
				}
				dCloseODE();
				isInitialized = false;
			}
		};
	}
}